<?php

namespace App\Http\Middleware\Api;

use Closure;
use Exception;
use Illuminate\Http\Request;
use JetBrains\PhpStorm\NoReturn;

/**
 * API签名检测
 * Class CheckApiSignMiddleware
 * @package App\Http\Middleware
 */
class CheckApiSignMiddleware
{
    /**
     * 中间件处理方法
     * @param Request $request
     * @param Closure $next
     * @return mixed|void
     * @throws Exception
     */
	public function handle(Request $request, Closure $next)
    {
		if (env('IS_CHECK_SIGN', true)) {
			if ($this->checkSign($request->all())) {
				return $next($request);
			} else {
				$this->returnError("无权限访问");
			}
		} else {
			return $next($request);
		}
	}

    /**
     * 检查签名
     * @param array $param 请求参数列表
     * @return bool
     */
	public function checkSign(array $param): bool
    {
		$timeStr = $param['timestamp'] ?? "";
		if (!$timeStr) {
			return false;
		}

		//10分钟后签名失效
		if ($timeStr < (time() - 600)) {
			return false;
		}

		$sign = array_key_exists('sign', $param) ? $param['sign'] : false;
		if (!$sign) {
			return false;
		}

        $randstr = array_key_exists('randstr', $param) ? $param['randstr'] : false;
		if (!$randstr) {
			return false;
		}

        $platform = array_key_exists('platform', $param) ? $param['platform'] : false;
		if (!$platform) {
			return false;
		}

		$platformList = [
			'ios',
			'android',
			'h5',
			'pc'
		];

		if (!in_array($platform, $platformList)) {
			return false;
		}

		$tempParam = $this->filterData($param);
		$keyList = [];
        try {
                foreach ($tempParam as $key => $item) {
                $keyList[$key] = md5($key . '_' . $item);
            }
        } catch (\Exception $e) {
            return false;
        }

		asort($keyList);
		$keyStr = env('APP_SOCKET_KEY', 'SCARECROW_API_SOCKET');
		$checkSign = md5($keyStr . http_build_query($keyList) . $keyStr);
		if ($sign == $checkSign) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 格式化数据,移除数据中的sign参数，以及值为空的数据
	 * @param array $param 请求参数列表
	 * @return array
     */
	public function filterData(array $param): array
    {
		unset($param['sign']);
		foreach ($param as $k => $item) {
			if ($item === "" || $item === null || is_array($item) || is_object($item)) {
				unset($param[$k]);
			}
		}
		return $param;
	}

    /**
     * 返回错误信息
     * @param string $str 错误字符串
     * @param int $code 错误码
     * @throws Exception
     */
	#[NoReturn] protected function returnError(string $str = '签名验证失败', int $code = RETURN_CHECK_SIGN_FAIL): void
    {
		die(ApiReturnError($code, $str));
	}
}
